package com.ht.medicaltreatment.utils

import android.os.Handler
import android.os.Looper
import java.lang.reflect.Method
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors

/**
 * 线程模式定义
 */
enum class ThreadMode {
    MAIN,       // 主线程处理
    BACKGROUND, // 子线程处理
    POSTING     // 发送线程处理（默认）
}

/**
 * 订阅注解
 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class Subscribe(
    val threadMode: ThreadMode = ThreadMode.POSTING,
    val sticky: Boolean = false
)

/**
 * 订阅信息封装
 */
private data class Subscription(
    val subscriber: Any,        // 订阅者对象
    val method: Method,         // 订阅方法
    val eventType: Class<*>,    // 事件类型
    val threadMode: ThreadMode // 线程模式
)

/**
 * 轻量级事件总线
 */
object EventBusUtils {
    // 存储所有订阅关系：事件类型 → 订阅列表
    private val subscriptions = ConcurrentHashMap<Class<*>, MutableList<Subscription>>()

    // 存储粘性事件：事件类型 → 事件对象
    private val stickyEvents = ConcurrentHashMap<Class<*>, Any>()

    // 主线程 Handler
    private val mainHandler = Handler(Looper.getMainLooper())

    // 子线程池
    private val backgroundExecutor = Executors.newCachedThreadPool()

    /**
     * 注册订阅者
     */
    fun register(subscriber: Any) {
        synchronized(subscriptions) {
            // 避免重复注册
            if (subscriptions.any { it.value.any { sub -> sub.subscriber == subscriber } }) return

            subscriber.javaClass.declaredMethods.forEach { method ->
                method.getAnnotation(Subscribe::class.java)?.let { annotation ->
                    // 验证方法参数（必须为单个参数）
                    val params = method.parameterTypes
                    if (params.size != 1) {
                        throw IllegalArgumentException("Subscribe method must have exactly 1 parameter")
                    }

                    val eventType = params[0]
                    val subscription = Subscription(subscriber, method, eventType, annotation.threadMode)

                    // 添加到订阅列表
                    subscriptions.getOrPut(eventType) { mutableListOf() }.add(subscription)

                    // 处理粘性事件
                    if (annotation.sticky) {
                        stickyEvents[eventType]?.let { postInternal(it, true) }
                    }
                }
            }
        }
    }

    /**
     * 注销订阅者
     */
    fun unregister(subscriber: Any) {
        synchronized(subscriptions) {
            subscriptions.values.forEach { list ->
                list.removeAll { it.subscriber == subscriber }
            }
        }
    }

    /**
     * 发送普通事件
     */
    fun post(event: Any) {
        postInternal(event, false)
    }

    /**
     * 发送粘性事件
     */
    fun postSticky(event: Any) {
        stickyEvents[event.javaClass] = event
        postInternal(event, true)
    }

    // 内部事件处理
    private fun postInternal(event: Any, isSticky: Boolean) {
        val eventType = event.javaClass
        subscriptions[eventType]?.forEach { subscription ->
            // 粘性事件只发送给标记为 sticky 的订阅者
            if (!isSticky || subscription.method.getAnnotation(Subscribe::class.java)?.sticky == true) {
                invokeMethod(subscription, event)
            }
        }
    }

    // 根据线程模式调用方法
    private fun invokeMethod(subscription: Subscription, event: Any) {
        when (subscription.threadMode) {
            ThreadMode.MAIN -> {
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    invoke(subscription, event)
                } else {
                    mainHandler.post { invoke(subscription, event) }
                }
            }

            ThreadMode.BACKGROUND -> {
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    backgroundExecutor.execute { invoke(subscription, event) }
                } else {
                    invoke(subscription, event)
                }
            }

            ThreadMode.POSTING -> invoke(subscription, event)
        }
    }

    // 反射调用方法（带异常捕获）
    private fun invoke(subscription: Subscription, event: Any) {
        try {
            subscription.method.isAccessible = true
            subscription.method.invoke(subscription.subscriber, event)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}